package com.suisung.shopsuite.account.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.server.HttpServerResponse;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qcloud.cos.utils.Md5Utils;
import com.squareup.okhttp.*;
import com.squareup.okhttp.RequestBody;
import com.suisung.shopsuite.account.controller.front.Test;
import com.suisung.shopsuite.account.model.entity.UserBase;
import com.suisung.shopsuite.account.model.entity.UserBindConnect;
import com.suisung.shopsuite.account.model.input.RegInput;
import com.suisung.shopsuite.account.model.req.*;
import com.suisung.shopsuite.account.model.res.LoginPolicyRes;
import com.suisung.shopsuite.account.model.res.LoginRes;
import com.suisung.shopsuite.account.model.res.LogoutRes;
import com.suisung.shopsuite.account.repository.UserBaseRepository;
import com.suisung.shopsuite.account.repository.UserBindConnectRepository;
import com.suisung.shopsuite.account.service.LoginService;
import com.suisung.shopsuite.common.config.ConfigProperties;
import com.suisung.shopsuite.common.consts.BindConnectCode;
import com.suisung.shopsuite.common.consts.ConstantMsg;
import com.suisung.shopsuite.common.exception.BusinessException;
import com.suisung.shopsuite.common.utils.CheckUtil;
import com.suisung.shopsuite.common.utils.ContextUtil;
import com.suisung.shopsuite.common.utils.RSAUtil;
import com.suisung.shopsuite.common.utils.phone.PhoneModel;
import com.suisung.shopsuite.common.utils.phone.PhoneNumberUtils;
import com.suisung.shopsuite.common.web.service.VerifyCodeService;
import com.suisung.shopsuite.core.web.CommonRes;
import com.suisung.shopsuite.core.web.controller.BaseController;
import com.suisung.shopsuite.pay.service.WeChatPayInfo;
import com.suisung.shopsuite.sys.service.ConfigBaseService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.Md5Crypt;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;

import static com.suisung.shopsuite.common.utils.I18nUtil.__;

/**
 * <p>
 * 用户基本信息表 前端控制器
 * </p>
 *
 * @author Xinze
 * @since 2022-11-27
 */
@Api(tags = "登录接口")
@RestController
@RequestMapping("/front/account/login/")
@Slf4j
public class LoginController extends BaseController {
    @Autowired
    private LoginService loginService;

    @Autowired
    private UserBindConnectRepository userBindConnectRepository;

    @Autowired
    private UserBaseRepository userBaseRepository;


    @Autowired
    private VerifyCodeService verifyCodeService;

    @Resource
    private ConfigProperties configProperties;

    @Autowired
    private ConfigBaseService configBaseService;

    /**
     * https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/qrcode-link/url-scheme/generateScheme.html
     * 获取加密scheme码
     * 生成已发布的小程序的 URL Scheme
     * @param shareReq
     * @return
     * @throws Exception
     */
    @ApiOperation(value = "分享小程序", notes = "生成已发布的小程序的 URL Scheme")
    @RequestMapping(value = "/shareMini", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<?> shareMini(ShareReq shareReq) throws Exception {
        log.info("shareMini request {}", JSON.toJSONString(shareReq));

        try {
            URL url = new URL("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + WeChatPayInfo.appid + "&secret=" + WeChatPayInfo.secret);

            InputStream inputStream = url.openStream();
            BufferedInputStream inputStreamReader = new BufferedInputStream(inputStream);
            InputStreamReader reader = new InputStreamReader(inputStreamReader);

            char[] bytes = new char[2048];
            int length = -1;

            StringBuilder sx = new StringBuilder();
            while (-1 != (length = reader.read(bytes))) {
                String s = new String(bytes, 0, length);
                sx.append(s);
            }

            log.info("shareMini accesstoken response is {}", sx.toString());

            MediaType mediaType
                    = MediaType.parse("application/json; charset=utf-8");

            OkHttpClient client = new OkHttpClient();

            String access_token = JSON.parseObject(sx.toString()).getString("access_token");

            inputStreamReader.close();


            String getwxacodeUrl = "https://api.weixin.qq.com/wxa/generatescheme?access_token=" + access_token;

            String path = shareReq.getPath();
            String query = "";
            if (StringUtils.isNotBlank(path) && path.contains("?") && path.substring(path.indexOf("?")).length() > 1) {
                query = path.substring(path.indexOf("?") + 1, path.length());
                path = path.substring(0, path.indexOf("?"));
            }
            String requestBodystring = JSON.toJSONString(new WechatGenerateschemeReq(WechatGenerateschemeReq.JumpWxa.builder().path(path).query(query).env_version("trial").build()));
            log.info("shareMini generatescheme request is {}", requestBodystring);
            RequestBody body = RequestBody.create(mediaType, requestBodystring);
            Request request = new Request.Builder()
                    .url(getwxacodeUrl)
                    .post(body)
                    .build();
            Response response = client.newCall(request).execute();

            String result = response.body().string();
            log.info("shareMini generatescheme response is {}", result);
            JSONObject jsonObject = JSON.parseObject(result);
            return success(jsonObject.getString("openlink"));
        } catch (Exception e) {
            log.error("shareMini error " + e.getMessage(), e);
            return fail("shareMini error");
        }

    }



//    @ApiOperation(value = "分享小程序", notes = "分享小程序")
//    @RequestMapping(value = "/shareMini", method = {RequestMethod.POST, RequestMethod.GET})
//    public CommonRes<?> shareMini(ShareReq shareReq) throws Exception {
//        log.info("share mini request {}", JSON.toJSONString(shareReq));
//
//        URL url = new URL("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wxac7c6548bb397005&secret=92593495abc5f703ffcea0efeb59b7b0");
//
//        InputStream inputStream = url.openStream();
//        BufferedInputStream inputStreamReader = new BufferedInputStream(inputStream);
//        InputStreamReader reader = new InputStreamReader(inputStreamReader);
//
//        char[] bytes = new char[2048];
//        int length = -1;
//
//        StringBuilder sx = new StringBuilder();
//        while (-1 != (length = reader.read(bytes))) {
//            String s = new String(bytes, 0, length);
//            sx.append(s);
//        }
//
//        log.info("shareMini accesstoken response is {}", sx.toString());
//
//        MediaType mediaType
//                = MediaType.parse("application/json; charset=utf-8");
//
//        OkHttpClient client = new OkHttpClient();
//
//        String access_token = JSON.parseObject(sx.toString()).getString("access_token");
//
//        inputStreamReader.close();
//
//        String getwxacodeUrl = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + access_token;
//        RequestBody body = RequestBody.create(mediaType, JSON.toJSONString(new Test.MiniDTO(access_token, shareReq.getPath())));
//        Request request = new Request.Builder()
//                .url(getwxacodeUrl)
//                .post(body)
//                .build();
//        Response response = client.newCall(request).execute();
//
//        InputStream ist = response.body().byteStream();
//
//        String fileName = Md5Utils.md5Hex(shareReq.getPath().getBytes());
//        fileName = fileName.substring(fileName.length() - 12, fileName.length());
//
//        String filePath = "/home/" + fileName + ".png";
//        FileOutputStream fileOutputStream = new FileOutputStream(new File(filePath));
//        byte[]  codebyte = new byte[2048];
//        length = -1;
//        while (-1 != (length = ist.read(codebyte))) {
//            fileOutputStream.write(codebyte, 0, length);
//            fileOutputStream.flush();
//        }
//
//        fileOutputStream.close();
//        ist.close();
//
//        String pictureUrl = "http://101.35.222.177:18101/front/account/login/shareMiniPicture/" + fileName;
//        return success(pictureUrl);
//    }

    @RequestMapping(value = "/shareMiniPicture/{pictureName}", method = {RequestMethod.POST, RequestMethod.GET})
    public void shareMiniPicture(@PathVariable("pictureName") String pictureName, HttpServletResponse response) {
        log.info("shareMiniPicture param is {}", pictureName);

        try {
            FileInputStream fileInputStream = new FileInputStream(new File("/home/" + pictureName + ".png"));
            response.setContentType("image/jpeg");
            ServletOutputStream outputStream = response.getOutputStream();
            byte[] bs = new byte[2048];
            int length = -1;
            while (-1 != (length = fileInputStream.read(bs))) {
                outputStream.write(bs, 0, length);
                outputStream.flush();
            }
        } catch (Exception e) {
            log.error("shareMiniPicture error " + e.getMessage(), e);
        }
    }

    @ApiOperation(value = "登录接口", notes = "登录接口")
    @RequestMapping(value = "/login", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<?> login(LoginReq loginReq) throws Exception {
        //loginReq 数据验证
        try {
            if (loginReq.getEncrypt()) {
                String decrypt = RSAUtil.decrypt(loginReq.getPassword(), configBaseService.getConfig("private_key", configProperties.getPrivateKey()));
                loginReq.setPassword(decrypt);
            }

            //验证码
            if (CheckUtil.isNotEmpty(loginReq.getVerifyKey())) {
                if (!verifyCodeService.checkVerifyCode(loginReq.getVerifyKey(), loginReq.getVerifyCode())) {
                    return fail(__("验证码有误"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
                }
            }

            LoginRes loginRes = loginService.login(loginReq);
            return success(loginRes);
        } catch (BusinessException be) {
            return fail(be.getMessage());
        } catch (Exception e) {
            log.error("login error is " + e.getMessage(), e);
            return fail("登录失败");
        }

    }


    @ApiOperation(value = "短信登录接口", notes = "短信登录接口")
    @RequestMapping(value = "/doSmsLogin", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<?> doSmsLogin(RegReq regReq) {
        //判断手机号
        PhoneModel phoneModelWithCountry = PhoneNumberUtils.getPhoneModelWithCountry(regReq.getVerifyKey());

        if (phoneModelWithCountry == null) {
            return fail(__("手机号码不正确"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
        }

        //验证码
        if (!verifyCodeService.checkVerifyCode(regReq.getVerifyKey(), regReq.getVerifyCode())) {
            return fail(__("验证码有误"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
        }

        RegInput input = new RegInput();
        input.setUserIntl(phoneModelWithCountry.getCountryCodeStr());
        input.setUserMobile(phoneModelWithCountry.getNationalNumber());
        input.setBindType(BindConnectCode.MOBILE);

        LoginRes loginRes = loginService.doSmsLogin(input);

        return success(loginRes);
    }

    @ApiOperation(value = "注册接口", notes = "注册接口")
    @RequestMapping(value = "/register", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<?> register(RegReq regReq) {
        //loginReq 数据验证
        log.info("register param is {}", JSON.toJSONString(regReq));

        //验证码
        if (!verifyCodeService.checkVerifyCode(regReq.getVerifyKey(), regReq.getVerifyCode())) {
            return fail(__("验证码有误"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
        }

        try {
            RegInput input = BeanUtil.copyProperties(regReq, RegInput.class);
            input.setUserAccount(regReq.getUserAccount());
            input.setPassword(regReq.getPassword());
            input.setEncrypt(regReq.getEncrypt());

            input.setUserParentId(regReq.getSourceUserId());

            Integer userId = loginService.register(input);
            log.info("register success userid {}, param is {}", userId, JSON.toJSONString(regReq));
            CommonRes<LoginRes> success = success(loginService.login(userId));
            log.info("register success login userid {}, login response is {}", userId, JSON.toJSONString(success));

            return success;
        } catch (BusinessException be) {
            return fail(be.getMessage());
        } catch (Exception e) {
            log.error("register error is " + e.getMessage(), e);
            return fail("注册失败");
        }
    }


    @ApiOperation(value = "重设密码接口", notes = "重设密码接口")
    @RequestMapping(value = "/setNewPassword", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<?> setNewPassword(ResetPasswordReq req) {
        //验证码
        if (!verifyCodeService.checkVerifyCode(req.getVerifyKey(), req.getVerifyCode())) {
            return fail(__("验证码有误"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
        }

        if (CheckUtil.isEmpty(req.getPassword())) {
            return fail(__("密码有误"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
        }


        Integer userId = null;


        if (req.getBindType().intValue() == BindConnectCode.MOBILE) {
            //判断手机号
            PhoneModel phoneModelWithCountry = PhoneNumberUtils.getPhoneModelWithCountry(req.getVerifyKey());

            if (phoneModelWithCountry == null) {
                return fail(__("手机号码不正确"), ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED);
            }

            String bindId = String.format("%s%d", phoneModelWithCountry.getCountryCodeStr(), phoneModelWithCountry.getNationalNumber());

            //判断是否绑定
            UserBindConnect userBindConnect = userBindConnectRepository.get(bindId);

            if (ObjectUtil.isNotEmpty(userBindConnect)) {
                userId = userBindConnect.getUserId();
            } else {
                return fail(__("手机号码未绑定登录"));
            }
        } else if (req.getBindType().intValue() == BindConnectCode.EMAIL) {
            if (!CheckUtil.isEmail(req.getVerifyKey())) {
                return fail(__("该邮箱不存在"));
            }

            //判断是否绑定
            UserBindConnect userBindConnect = userBindConnectRepository.get(req.getVerifyKey());

            if (ObjectUtil.isNotEmpty(userBindConnect)) {
                userId = userBindConnect.getUserId();
            } else {
                return fail(__("该邮箱绑定不存在"));
            }
        } else if (req.getBindType().intValue() == BindConnectCode.ACCOUNT) {
            userId = ContextUtil.checkLoginUserId();

            UserBase user = userBaseRepository.get(userId);

            if (!user.getUserPassword().equals(loginService.encodePassword(req.getOldPassword(), user.getUserSalt()))) {
                throw new BusinessException(ConstantMsg.CODE_BUSINESS_VALIDATION_FAILED, __("旧密码不正确"));
            }
        }

        loginService.doResetPasswd(userId, req.getPassword());

        return success();
    }

    @ApiOperation(value = "修改密码", notes = "修改密码")
    @RequestMapping(value = "/changePassword", method = RequestMethod.POST)
    public CommonRes<?> changePassword(ResetPasswordReq req) {

        if (CheckUtil.isEmpty(req.getPassword())) {
            throw new BusinessException(__("密码有误！"));
        }

        Integer userId = ContextUtil.checkLoginUserId();
        UserBase user = userBaseRepository.get(userId);

        if (!user.getUserPassword().equals(loginService.encodePassword(req.getOldPassword(), user.getUserSalt()))) {
            throw new BusinessException(__("旧密码不正确！"));
        }

        loginService.doResetPasswd(userId, req.getPassword());

        return success();
    }

    @ApiOperation(value = "退出登录", notes = "退出登录")
    @RequestMapping(value = "/logout", method = {RequestMethod.POST, RequestMethod.GET})
    public CommonRes<LogoutRes> logout(LoginReq loginReq) {
        LogoutRes logoutRes = loginService.logout();

        return success(logoutRes);
    }

    @ApiOperation(value = "隐私政策", notes = "隐私政策")
    @RequestMapping(value = "/protocol", method = RequestMethod.GET)
    public CommonRes<LoginPolicyRes> protocol(@RequestParam(name = "document_type", required = false) String documentType,
                                              @RequestParam(name = "state", required = false) Integer state,
                                              @RequestParam(name = "protocols_key", required = false, defaultValue = "reg_protocols_description") String protocolsKey) {
        LoginPolicyRes loginPolicyRes = loginService.protocol(documentType, state, protocolsKey);

        return success(loginPolicyRes);
    }
}

